/* vim:ft=objc:
 * $Id: Growl.m 146 2005-05-24 22:35:23Z bmoore $
 *
 * Copyright (c) 2004, 2005  Branden J. Moore.
 *
 * This file is part of MacBiff, and is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * MacBiff is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with MacBiff; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307 USA.
 *
 */

#import <Cocoa/Cocoa.h>
#import <AddressBook/ABAddressBook.h>
#import <AddressBook/ABImageLoading.h>
#import <AddressBook/ABPerson.h>
#import "Growl.h"
#import <Growl/GrowlApplicationBridge.h>

#import <MacBiffPlugIn/PreferencesController.h>
#import <MacBiffPlugIn/debug.h>

#define APP_NAME @"MacBiff"
#define NOTIFY_NAME @"New Mail"
#define PLUGIN_NAME @"Growl"



@implementation Growl

+ (NSString*) plugInName
{
	return PLUGIN_NAME;
}

+ (NSString*) plugInVersion
{
	return @"1.0.0";
}

+ (NSString*) plugInAuthor
{
	return @"Branden J. Moore <bmoore@forkit.org>";
}

+ (NSString*) plugInUrl
{
	return @"http://www.forkit.org/macbiff/";
}


- (id) init
{
	dprintf("Initializing Growl PlugIn.\n");
	if ( ( self = [super init] ) != Nil ) {

		dprintf("Loading Growl's NIB.\n");
		if ( ![NSBundle loadNibNamed: @"Growl" owner: self] ) {
			alert("Unable to load Growl's Nib\n");
			return nil;
		}

		NSImage *myIcon = [NSImage imageNamed: @"mailbox"];
		iconData = [[myIcon TIFFRepresentation] retain];
		notificationNames = [[NSArray alloc] initWithObjects:
			NOTIFY_NAME, nil];

		alert("Growl Status: Installed: %s\t Running: %s\n",
			[GrowlApplicationBridge isGrowlInstalled] ? "YES" :"NO",
			[GrowlApplicationBridge isGrowlRunning] ? "YES" : "NO");
		dprintf("Registering with Growl\n");
		[GrowlApplicationBridge setGrowlDelegate: self];


	}
	return self;
}


- (NSDictionary*) registrationDictionaryForGrowl
{
	dprintf("Giving our Growl Registration.\n");
	NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
		notificationNames, GROWL_NOTIFICATIONS_DEFAULT,
		notificationNames, GROWL_NOTIFICATIONS_ALL,
		nil];
	return info;
}


- (NSString *) applicationNameForGrowl
{
	return APP_NAME;
}


- (NSData *) applicationIconDataForGrowl
{
	return iconData;
}


- (void) growlIsReady
{
	dprintf("I was just told that Growl is Ready.\n");
}


- (void) growlNotificationWasClicked: (id) clickContext
{
	dprintf("Notification was clicked.\n");
	NSString *urlStr = [clickContext objectForKey: @"URL"];
	if ( urlStr && ![urlStr isEqualToString: @""] ) {
		dprintf("opening url: '%s'\n", [urlStr cString]);
		[[NSWorkspace sharedWorkspace]
			openURL: [NSURL URLWithString: urlStr]];
	} else {
		[[NSWorkspace sharedWorkspace] launchApplication:
			[[prefs misc] objectForKey: @"Mail App"]];
	}

}


- (void) dealloc
{
	[iconData release];
	[notificationNames release];

	[super dealloc];
}



- (void) setupView
{
	dprintf("Setting up the View for Growl.\n");

	[titleStringField setStringValue: [[prefs action: PLUGIN_NAME]
			objectForKey: @"Title String"]];
	[stringField setStringValue: [[prefs action: PLUGIN_NAME]
			objectForKey: @"String"]];

	int verb = [[[prefs action: PLUGIN_NAME]
		objectForKey: @"Notify Verbosity"] intValue];
	[verbosityPopUp selectItemAtIndex: verb];
	[verbosityPopUp synchronizeTitleAndSelectedItem];
	[perFolderField setHidden: (verb == 0)];
	[perMessageField setHidden: (verb != 2)];
	return;
}


- (void) preActionForAccount: (Account*)acct
{
	return;
}


- (void) postGrowlString: (NSString*) desc withTitle: (NSString*) title andIcon: (NSData*) icon andClickContext: (NSDictionary*) context
{
	[GrowlApplicationBridge notifyWithTitle: title
				    description: desc
			       notificationName: NOTIFY_NAME
				       iconData: icon
				       priority: 0.0
				       isSticky: NO
				   clickContext: context];
}


- (NSData*) iconForEmail: (NSString*) email
{
	ABAddressBook *AB = [ABAddressBook sharedAddressBook];
	ABSearchElement *se = Nil;
	NSString *emailStr = Nil;
	if ( [email characterAtIndex: 0] == '<' ) {
		emailStr = [email substringWithRange: NSMakeRange(1, [email length] -2)];
	} else {
		emailStr = email;
	}
	dprintf("checking for email: %s\n", [emailStr cString]);

	se = [ABPerson searchElementForProperty: kABEmailProperty
			label: Nil key: Nil value: emailStr
			comparison: kABContainsSubStringCaseInsensitive];
	NSArray *found = [AB recordsMatchingSearchElement: se];
	if ( [found count] == 0 ) {
		dprintf("No people found.\n");
		return iconData;
	} else {
		dprintf("%d people found.\n", [found count]);
		ABPerson *person = [found objectAtIndex: 0];
		NSData *imgData = [person imageData];
		if ( imgData && [imgData length] > 0 ) {
			dprintf("using custom icon\n");
			return imgData;
		} else {
			return iconData;
		}
	}
}


- (void) postActionForAccount: (Account*) acct
{
	NSString *desc = Nil;
	NSString *title = Nil;
	NSData *icon = iconData;
	NSArray *newMailFolders = [acct foldersWithNewMailIncludingMasked: NO];
	NSDictionary *context = [NSDictionary dictionary];


	if ( [newMailFolders count] == 0 ) return;


	int verb = [[[prefs action: PLUGIN_NAME]
		objectForKey: @"Notify Verbosity"] intValue];

	title = [self buildString: [[prefs action: PLUGIN_NAME]
				objectForKey: @"Title String"]
			fromAccount: acct];
	desc = [self buildString: [[prefs action: PLUGIN_NAME]
				objectForKey: @"String"]
			fromAccount: acct];

	if ( verb == 0 ) {
		/* Single announcement */
		[self postGrowlString: desc withTitle: title andIcon: icon andClickContext: context];
	} else {
		NSString *subdesc = Nil;
		NSString *subtitle = Nil;
		NSEnumerator *folderEnum = [newMailFolders objectEnumerator];
		Folder *folder = Nil;

		while ( (folder = [folderEnum nextObject]) ) {
			dprintf("Growling for folder: %s\n",
					[[folder fullPath] cString]);
			subtitle = [self buildString: title
					fromFolder: folder];
			subdesc = [self buildString: desc
					fromFolder: folder];
			if ( verb == 1 ) {
				/* Per folder announcements */
				[self postGrowlString: subdesc
					withTitle: subtitle
					  andIcon: icon
				  andClickContext: context];
			} else {
				/* Per message announcements */
				NSString *mtitle = Nil;
				NSString *mdesc = Nil;
				NSEnumerator *msgEnum = [[folder messageList]
					objectEnumerator];
				Message *msg = Nil;


				while ( (msg = [msgEnum nextObject]) ) {
					if ( ![msg new] ) continue;
					icon = [self iconForEmail: [msg email]];
					mtitle = [self buildString: subtitle
						fromMessage: msg];
					mdesc = [self buildString: subdesc
						fromMessage: msg];
					context = [NSDictionary dictionaryWithObjectsAndKeys: [msg url], @"URL", Nil];
					[self postGrowlString: mdesc
						withTitle: mtitle
					  	andIcon: icon
						andClickContext: context];
				}
			}
		}
	}
}


- (NSString*) buildString: (NSString*) oStr fromAccount: (Account*) acct
{
/*
 * Preference String Format:
 *
 * %a : Account Name
 * %n : # New Messages
 * %u : # Unread Messages
 * %t : Total # of Messages
 * %F : # of folders with Unread Messages
 * %f : # of folders with New Messages
 * %L : List of folders with Unread Messages
 * %l : List of folders with New Messages
 */
	NSRange range;
	NSMutableString *str = [NSMutableString stringWithCapacity: 32];
	NSMutableString *flist = [NSMutableString stringWithCapacity: 32];
	NSArray *arr;
	unsigned int i;

	dprintf("%s starting.\n", __func__);

	[str setString: oStr];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%a" withString: [acct name]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%n"
		withString: [NSString stringWithFormat: @"%u", [acct new]]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%u"
		withString: [NSString stringWithFormat: @"%u", [acct unread]]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%t"
		withString: [NSString stringWithFormat: @"%u", [acct total]]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%F"
			     withString: [NSString stringWithFormat: @"%u",
				[[acct foldersWithUnreadMailIncludingMasked: NO]
					count]]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%f"
			     withString: [NSString stringWithFormat: @"%u",
				[[acct foldersWithNewMailIncludingMasked: NO]
					count]]
		options: NSLiteralSearch range: range];

	[flist setString: @""];
	arr = [acct foldersWithUnreadMailIncludingMasked: NO];
	if ( [arr count] > 0 ) {
		[flist setString: [[arr objectAtIndex: 0] name]];
		for ( i = 1 ; i < [arr count] ; ++i )
			[flist appendFormat: @", %@", [[arr objectAtIndex: i] fullPath]];

	}
	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%L"
		withString: flist options: NSLiteralSearch range: range];

	[flist setString: @""];
	arr = [acct foldersWithNewMailIncludingMasked: NO];
	if ( [arr count] > 0 ) {
		[flist setString: [[arr objectAtIndex: 0] name]];
		for ( i = 1 ; i < [arr count] ; ++i )
			[flist appendFormat: @", %@", [[arr objectAtIndex: i] fullPath]];

	}
	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%l"
		withString: flist options: NSLiteralSearch range: range];

	dprintf("%s ending.\n", __func__);
	return str;
}


- (NSString*) buildString: (NSString*) oStr fromFolder: (Folder*) folder
{
/* %d : Folder Name
 * %N : # of New Messages in This Folder
 * %U : # of Unread Messages in This Folder
 */
	NSRange range;
	NSMutableString *str = [NSMutableString stringWithCapacity: 32];
	dprintf("%s starting.\n", __func__);

	[str setString: oStr];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%d" withString: [folder fullPath]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%N"
		withString: [NSString stringWithFormat: @"%u",
			[folder newMail]]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%U"
		withString: [NSString stringWithFormat: @"%u",
			[folder unreadMail]]
		options: NSLiteralSearch range: range];

	dprintf("%s ending.\n", __func__);
	return str;
}



- (NSString*) buildString: (NSString*) oStr fromMessage: (Message*) msg
{
/* %s : Sender's Name
 * %S : Subject
 */
	NSRange range;
	NSMutableString *str = [NSMutableString stringWithCapacity: 32];
	dprintf("%s starting.\n", __func__);

	[str setString: oStr];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%s"
		withString: [msg from]
		options: NSLiteralSearch range: range];

	range = NSMakeRange(0, [str length]);
	[str replaceOccurrencesOfString: @"%S"
		withString: [msg subject]
		options: NSLiteralSearch range: range];

	dprintf("%s ending.\n", __func__);
	return str;
}


- (IBAction) setTitleString: (id) sender
{
	[[prefs action: PLUGIN_NAME] setObject: [titleStringField stringValue]
		forKey: @"Title String"];
	[prefs commitActions];
}


- (IBAction) setNotifyString: (id) sender
{
	[[prefs action: PLUGIN_NAME] setObject: [stringField stringValue]
		forKey: @"String"];
	[prefs commitActions];
}


- (IBAction) setVerbosity: (id) sender
{
	int verb = [verbosityPopUp indexOfSelectedItem];
	if ( verb < 0 ) {
		verb = 0;
		[verbosityPopUp selectItemAtIndex: 0];
		[verbosityPopUp synchronizeTitleAndSelectedItem];
	}

	[[prefs action: PLUGIN_NAME] setObject: [NSNumber numberWithInt: verb]
		forKey: @"Notify Verbosity"];
	[prefs commitActions];

	[perFolderField setHidden: (verb == 0)];
	[perMessageField setHidden: (verb != 2)];
}


@end


